package org.xx.armory.db.impl;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.xx.armory.commons.Converter;
import org.xx.armory.db.MatchType;
import org.xx.armory.db.ParameterBuilder;
import org.xx.armory.db.ParameterDirection;
import org.xx.armory.db.ParameterType;
import org.xx.armory.db.Session;

import java.math.BigDecimal;
import java.util.Date;
import java.util.EnumSet;
import java.util.Set;

import static org.xx.armory.commons.Validators.notBlank;
import static org.xx.armory.commons.Validators.notNull;

/**
 * SQL参数构造器。
 *
 * @author Haart
 */
abstract class AbstractParameterBuilder
        implements ParameterBuilder {
    private final String name;
    private final Session session;
    private ParameterType type;
    private Object value;
    private MatchType match;
    private int size;
    private Set<ParameterDirection> directions;

    /**
     * 构造参数构造器对象。
     *
     * @param session
     *         生成构造器的Session。
     * @param name
     *         参数名称。
     * @throws IllegalArgumentException
     *         如果参数{@code session}是{@code null}或者{@code name}是{@code null}或者参数{@code name}只包含空白字符。
     */
    protected AbstractParameterBuilder(
            Session session,
            String name
    ) {
        this(session, name, null);
    }

    /**
     * 构造参数构造器对象。
     *
     * @param session
     *         生成构造器的Session。
     * @param name
     *         参数名称。
     * @param value
     *         参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code session}是{@code null}或者{@code name}是{@code null}或者参数{@code name}只包含空白字符。
     */
    protected AbstractParameterBuilder(
            Session session,
            String name,
            Object value
    ) {
        this(session, name, ParameterType.STRING, value == null ? null : String.valueOf(value));
    }

    /**
     * 构造参数构造器对象。
     *
     * @param session
     *         生成构造器的Session。
     * @param name
     *         参数名称。
     * @param type
     *         参数类型。
     * @param value
     *         参数值。
     * @throws IllegalArgumentException
     *         如果参数{@code session}是{@code null}或者{@code name}是{@code null}或者参数{@code type}是{@code null}或者参数{@code name}只包含空白字符。
     */
    protected AbstractParameterBuilder(
            Session session,
            String name,
            ParameterType type,
            Object value
    ) {
        notNull(session, "session");
        notNull(type, "type");

        this.session = session;
        this.name = notBlank(name, "name").trim();
        this.type = type;
        this.value = value;

        this.match = MatchType.EXACT;
        this.size = 0;
        this.directions = EnumSet.of(ParameterDirection.IN);
    }

    /**
     * 获取参数名。
     *
     * @return 参数名。
     */
    public final String getName() {
        return this.name;
    }

    /**
     * 获取参数类型。
     *
     * @return 参数类型。
     */
    public final ParameterType getType() {
        return this.type;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final ParameterBuilder setType(
            ParameterType type
    ) {
        this.type = type;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final ParameterBuilder setSize(
            int size
    ) {
        this.size = size;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final ParameterBuilder setMatch(
            MatchType match
    ) {
        this.match = match;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final ParameterBuilder setDirections(
            ParameterDirection... directions
    ) {
        final EnumSet<ParameterDirection> ds = EnumSet.noneOf(ParameterDirection.class);

        if (directions != null) {
            for (final ParameterDirection d : directions) {
                if (d != null) {
                    ds.add(d);
                }
            }
            this.directions = ds.isEmpty() ? EnumSet.of(ParameterDirection.IN) : ds;
        }

        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final ParameterBuilder setValue(
            Object value
    ) {
        this.value = value;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Session then() {
        session.addParameter(build());
        return this.session;
    }

    /**
     * 获取参数值。
     *
     * @return 参数值。
     */
    public final Object getValue() {
        return this.value;
    }

    /**
     * 获取参数匹配方式。
     *
     * @return 参数匹配方式。
     */
    public final MatchType getMatch() {
        return this.match;
    }

    /**
     * 获取参数值的长度。
     *
     * @return 参数值的长度。
     */
    public final int getSize() {
        return this.size;
    }

    /**
     * 获取参数传入传出方式。
     *
     * @return 参数传入传出方式。
     */
    public final Set<ParameterDirection> getDirections() {
        return this.directions;
    }

    /**
     * 执行构造SQL参数前的准备工作。包括检查各个参数的有效性。
     */
    protected final void beforeBuild() {
        int nSize = 0;

        if (this.value != null) {
            switch (this.type) {
                case STRING:
                    if (this.value instanceof CharSequence) {
                        final String s = this.value.toString();
                        if (this.size == 0) {
                            this.value = s;
                            nSize = s.length();
                        } else if (this.size > 0) {
                            this.value = StringUtils.left(s, this.size);
                            nSize = this.size;
                        }
                    } else if (value instanceof Integer) {
                        this.value = ((Integer) this.value).longValue();
                        this.type = ParameterType.INTEGER;
                    } else if (value instanceof Long) {
                        this.type = ParameterType.INTEGER;
                    } else if (value instanceof BigDecimal) {
                        this.type = ParameterType.DECIMAL;
                    } else if (value instanceof Float) {
                        this.value = ((Float) this.value).doubleValue();
                        this.type = ParameterType.DOUBLE;
                    } else if (value instanceof Double) {
                        this.type = ParameterType.DOUBLE;
                    } else if (value instanceof Date) {
                        this.type = ParameterType.DATETIME;
                    } else if (value instanceof java.util.Calendar) {
                        this.value = ((java.util.Calendar) value).getTime();
                        this.type = ParameterType.DATETIME;
                    } else if (value instanceof Boolean) {
                        this.type = ParameterType.BOOLEAN;
                    } else if (value instanceof byte[]) {
                        this.type = ParameterType.BINARY;
                    } else {
                        final String s = value.toString();
                        if (this.size == 0) {
                            this.value = s;
                            nSize = s.length();
                        } else if (this.size > 0) {
                            this.value = StringUtils.left(s, this.size);
                            nSize = this.size;
                        }
                    }
                    break;
                case START_DATE:
                case START_MONTH:
                case START_YEAR:
                case END_DATE:
                case END_MONTH:
                case END_YEAR:
                case DATETIME:
                    if (value instanceof Date) {
                        // Date 类型本身不需要转换。
                    } else if (value instanceof Number) {
                        this.value = Converter.toDate((Number) value);
                    } else {
                        this.value = Converter.parseDate(String.valueOf(value));
                    }
                    break;
                case BOOLEAN:
                    if (value instanceof Boolean) {
                        // Boolean 类型本身不需要转换。
                    } else {
                        final String s = String.valueOf(value).toLowerCase();
                        this.value = "true".equals(s) || "yes".equals(s) || "on".equals(s)
                                || "1".equals(s);
                    }
                    break;
                case INTEGER:
                    if (this.value instanceof Long) {
                        // Long 类型本身不需要转换。
                    } else if (value instanceof Number) {
                        this.value = ((Number) value).longValue();
                    } else {
                        this.value = NumberUtils.toLong(String.valueOf(value));
                    }
                    break;
                case DOUBLE:
                    if (this.value instanceof Double) {
                        // Double 类型本身不需要转换。
                    } else if (value instanceof Number) {
                        this.value = ((Number) value).doubleValue();
                    } else {
                        this.value = NumberUtils.toDouble(String.valueOf(value));
                    }
                    break;
                case DECIMAL:
                    if (value instanceof BigDecimal) {
                        // BigDecimal 类型本身不需要转换。
                    } else {
                        try {
                            this.value = new BigDecimal(String.valueOf(value));
                        } catch (NumberFormatException ex) {
                            this.value = BigDecimal.ZERO;
                        }
                    }
                    break;
                case BINARY:
                    if (value instanceof byte[]) {
                        // byte[] 类型本身不需要转换。
                    } else {
                        try {
                            this.value = java.util.Base64.getDecoder().decode(String.valueOf(value));
                        } catch (IllegalArgumentException ex) {
                            ex.printStackTrace();
                        }
                    }
                default:
                    break;
            }
        }

        this.size = nSize;
    }
}
